package com.xgg.camera2sdk;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.util.Size;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class NoPreManager {
    @NonNull private Context mContext;
    private android.hardware.camera2.CameraManager mSysManager;
    private String mPhotoPath;
    private String mVideoPath;
    private File mVideoFile;
    private Size mPhotoSize;
    private Size mVideoSize;

    private Boolean mFlashSupported;
    private TakePhotoCallback mTakePhotoCallback;
    private RecordVideoCallback mRecordVideoCallback;
    private CameraCaptureSession mPhotoSession,mVideoSession;
    private MediaRecorder mMediaRecorder;
    private CameraDevice mCameraDevice;
    private ImageReader mImageReader;
    private Semaphore mCameraOpenCloseLock;
    private boolean mRecording = false;
    private int mToDo = 0;
    private final int TODO_NONE = 0;
    private final int TODO_PHOTO = 1;
    private final int TODO_STARTVIDEO = 2;
    private final int TODO_ENDVEDIO = 3;

    private Handler mLogHandler = null;

    //构造函数
    public NoPreManager(@NonNull Context context){
        mContext = context;
        mSysManager = (android.hardware.camera2.CameraManager)mContext.getSystemService(Context.CAMERA_SERVICE);
        mCameraOpenCloseLock = new Semaphore(1);
        startBackgroundThread();
    }

    //获取摄像头名字列表
    public String[] getCameraList(){
        try {
            if (mSysManager.getCameraIdList().length == 0)
                return null;
            else
                return mSysManager.getCameraIdList();
        }
        catch (CameraAccessException e){
            return null;
        }
    }

    //选择照相文件保存路径
    public void setPhotoPath(String path){
        mPhotoPath = path;
    }

    //选择视频文件保存路径
    public void setVideoPath(String path){
        mVideoPath = path;
    }

    //初始化摄像头信息
    private boolean initCameraInfo(String cameraId){
        try {
            CameraCharacteristics characteristics = mSysManager.getCameraCharacteristics(cameraId);
            StreamConfigurationMap configurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            if (configurationMap == null){
                return false;
            }
            Size[] photores = configurationMap.getOutputSizes(ImageFormat.JPEG);
            for (Size size : photores){
                //PostLog(String.format("分辨率候选为%d * %d" , size.getWidth(), size.getHeight()));
                if (size.getHeight() <= 1536 && size.getWidth() <= 2048)
                {
                    //官方采用的分辨率是2592*1952，在列表中查找最接近的
                    mPhotoSize = size;
                    break;
                }
            }
            Size[] videores = configurationMap.getOutputSizes(MediaRecorder.class);
            for (Size size : videores){
                //PostLog(String.format("视频分辨率候选为%d * %d" , size.getWidth(), size.getHeight()));
                if (size.getHeight() <= 480 && size.getWidth() <= 640)
                {
                    mVideoSize = size;
                    break;
                }
            }
            //获取闪光灯支持情况
            Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
            mFlashSupported = (available == null) ? false : available;
        }
        catch (Exception e){
            return false;
        }
        PostLog("初始化摄像头[" + cameraId + "]成功");
        return true;
    }

    //外部调用，照相
    public void takePhoto(String cameraId , @NonNull TakePhotoCallback callback){
        mTakePhotoCallback = callback;
        //检查当前摄像头状态，是否正在处理请求，是否正在录像
        if (mToDo != TODO_NONE){
            mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_INIT_FAIL);
            return;
        }
        //没有正在进行的请求
        if (!mRecording) {
            //并未在录像阶段
            mToDo = TODO_PHOTO;
            //初始化摄像头
            if (!initCameraInfo(cameraId)){
                //初始化失败
                mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_INIT_FAIL);
                mToDo = TODO_NONE;
                return;
            }
            //初始化摄像头成功
            //准备开摄像头
            if (!openCamera(cameraId)){
                mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_OPENCAMERA_FAIL);
                mToDo = TODO_NONE;
                return;
            }
            //开摄像头执行成功，具体执行结果请查看回调内容
            //至此，mTODO被修改了，信号量也被占用
        }
        else{
            //现在正在录像，直接开request
            PostLog("正在录像中，共用session");
            try {
                final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                captureBuilder.addTarget(mImageReader.getSurface());
                captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                if (mFlashSupported) {
                    captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                }
                mVideoSession.capture(captureBuilder.build(), mPhotoCaptureCallback, mBackgroundHandler);
            }
            catch (Exception e){
            }
        }
    }

    //外部调用，录像开关
    public void recordVideo(String cameraId , @NonNull RecordVideoCallback callback){
        mRecordVideoCallback = callback;
        if (mToDo != TODO_NONE){
            mRecordVideoCallback.OnRecordVideoFail(RecordVideoCallback.RTCODE_INIT_FAIL);
            return;
        }
        if (mRecording){
            //正在录像，决定停止录像
            mToDo = TODO_ENDVEDIO;
            try {
                mMediaRecorder.stop();
                mMediaRecorder.release();
                mMediaRecorder = null;
                mVideoSession.close();
                mVideoSession = null;
                PostLog("结束录像");
                mRecordVideoCallback.OnRecordVideoEnded(mVideoFile.getAbsolutePath());
                closeCamera();
            }
            catch (Exception e){

            }
            mRecording = false;
            mToDo = TODO_NONE;
        }
        else{
            //现在没有录像，开始录像
            //录像需要先准备文件
            File Exist = new File(mVideoPath);
            if (!Exist.exists()) {
                if (!Exist.mkdirs())
                {
                    mRecordVideoCallback.OnRecordVideoFail(RecordVideoCallback.RTCODE_SAVE_FAIL);
                    return;
                }
            }
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
            mVideoFile = new File(Exist, formatter.format(new Date()) + ".mp4");
            mToDo = TODO_STARTVIDEO;
            //初始化摄像头
            if (!initCameraInfo(cameraId)){
                //初始化失败
                mRecordVideoCallback.OnRecordVideoFail(RecordVideoCallback.RTCODE_INIT_FAIL);
                mToDo = TODO_NONE;
                return;
            }
            //初始化摄像头成功
            //准备开摄像头
            if (!openCamera(cameraId)){
                mRecordVideoCallback.OnRecordVideoFail(RecordVideoCallback.RTCODE_OPENCAMERA_FAIL);
                mToDo = TODO_NONE;
                return;
            }
            //开摄像头执行成功，具体执行结果请查看回调内容
        }
    }

    //打开摄像头，这里不要擅自回调
    private boolean openCamera(String cameraId) {
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_EXCEPTION);
                return false;
            }
            if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
            else {
                mSysManager.openCamera(cameraId, mCameraStateCallback, mBackgroundHandler);
            }
        }
        catch (Exception e){
            return false;
        }
        PostLog("打开摄像头操作完成，等待CameraStateCallback");
        return true;
    }

    //摄像头状态变化回调
    private final CameraDevice.StateCallback mCameraStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            // This method is called when the camera is opened. We start camera preview here.
            mCameraDevice = cameraDevice;
            //PostLog("开启摄像头成功，CameraDevice初始化成功");
            if (mToDo == TODO_PHOTO && !mRecording) {
                //未录像状态下单拍一张照片
                PostLog("打开摄像头成功，准备建立session");
                startPhotoSession();
                mCameraOpenCloseLock.release();
                //至此，mTodo为photo，信号量已释放
            }
            else if (mToDo == TODO_STARTVIDEO){
                //开始录像
                PostLog("打开摄像头成功，准备建立session");
                startVideoSession();
                mCameraOpenCloseLock.release();
                //至此，mTodo为startvideo，信号量已释放
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            mCameraOpenCloseLock.release();
            //PostLog("与摄像头断开连接");
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            if (mToDo == TODO_PHOTO && !mRecording) {
                //未录像状态下单拍一张照片
                mCameraOpenCloseLock.release();
                mCameraDevice = cameraDevice;
                mToDo = TODO_NONE;
                closeCamera();
                mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_OPENCAMERA_FAIL);
            }
            else if (mToDo == TODO_STARTVIDEO){
                //开启录像
                mCameraOpenCloseLock.release();
                mCameraDevice = cameraDevice;
                mToDo = TODO_NONE;
                closeCamera();
                mRecordVideoCallback.OnRecordVideoFail(RecordVideoCallback.RTCODE_OPENCAMERA_FAIL);
            }
        }
    };

    //创建拍照的session
    private void startPhotoSession() {
        if (mImageReader == null) {
            mImageReader = ImageReader.newInstance(mPhotoSize.getWidth(), mPhotoSize.getHeight(), ImageFormat.JPEG, /*maxImages*/2);
            mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
            //PostLog("3-1 ImageReader初始化成功");
        }
        try {
            mCameraDevice.createCaptureSession(Arrays.asList(mImageReader.getSurface()),mPhotoSessionCallback ,null);
        }
        catch (Exception e) {
            mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_SESSION_FAIL);
            mToDo = TODO_NONE;
            closeCamera();
        }
    }

    //创建录像的session
    private void startVideoSession() {
        try {
            //录像输出目标
            mMediaRecorder = new MediaRecorder();
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mMediaRecorder.setVideoEncodingBitRate(10000000);
            mMediaRecorder.setVideoFrameRate(30);
            mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath());
            mMediaRecorder.prepare();
            PostLog("MediaRecorder配置完成");
            //录像过程中可能拍照，照片输出目标
            if (mImageReader == null) {
                mImageReader = ImageReader.newInstance(mPhotoSize.getWidth(), mPhotoSize.getHeight(), ImageFormat.JPEG, /*maxImages*/2);
                mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
                PostLog("ImageReader初始化成功");
            }
            mCameraDevice.createCaptureSession(Arrays.asList(mImageReader.getSurface(),mMediaRecorder.getSurface()),mVideoSessionCallback , mBackgroundHandler);
        }
        catch (Exception e){
            mRecordVideoCallback.OnRecordVideoFail(RecordVideoCallback.RTCODE_SESSION_FAIL);
            mToDo = TODO_NONE;
            closeCamera();
        }
    }

    //照相用session的回调
    private CameraCaptureSession.StateCallback mPhotoSessionCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
            mPhotoSession = cameraCaptureSession;
            PostLog("配置session成功，准备拍照");
            try {
                final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                captureBuilder.addTarget(mImageReader.getSurface());
                captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                if (mFlashSupported) {
                    captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                }
                mPhotoSession.capture(captureBuilder.build(), mPhotoCaptureCallback, mBackgroundHandler);
                //已经开始抓拍
            }
            catch (Exception e){
                mToDo = TODO_NONE;
                mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_CAPTURE_FAIL);
                closeCamera();
            }
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
            if (mToDo == TODO_PHOTO && !mRecording){
                //非录像状态下，单次拍照，配置session失败
                mToDo = TODO_NONE;
                mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_SESSION_FAIL);
                closeCamera();
            }
        }

        @Override
        public void onClosed (@NonNull CameraCaptureSession session){
            //PostLog("mPhotoSession关闭");
        }
    };

    //录像用session的回调
    private CameraCaptureSession.StateCallback mVideoSessionCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
            try {
                mVideoSession = cameraCaptureSession;
                CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
                builder.addTarget(mMediaRecorder.getSurface());
                builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
                mVideoSession.setRepeatingRequest(builder.build(), null, mBackgroundHandler);
                mMediaRecorder.start();
                PostLog("开始录像成功");
                mRecordVideoCallback.OnRecordVideoStarted();
                mRecording = true;
                mToDo = TODO_NONE;
            }
            catch (Exception e){
                PostLog(e.getLocalizedMessage());
            }
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
            if (mToDo == TODO_PHOTO && !mRecording){
                //非录像状态下，单次拍照，配置session失败
                mToDo = TODO_NONE;
                mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_SESSION_FAIL);
                closeCamera();
            }
            else if (mToDo == TODO_STARTVIDEO){
                PostLog("录像失败");
            }
        }

        @Override
        public void onClosed (@NonNull CameraCaptureSession session){
            //PostLog("mPhotoSession关闭");
        }
    };

    //拍照成功，判断是否要关闭摄像头，注意不在这里保存图片
    private CameraCaptureSession.CaptureCallback mPhotoCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,@NonNull CaptureRequest request,@NonNull TotalCaptureResult result) {
            PostLog("拍照完成，清理状态，准备关闭摄像头");
            mToDo = TODO_NONE;
            if (!mRecording) {
                closeCamera();
            }
        }
    };

    //图片就绪回调
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            File Exist = new File(mPhotoPath);
            if (!Exist.exists())
            {
                if (!Exist.mkdirs())
                {
                    mTakePhotoCallback.OnTakePhotoFail(TakePhotoCallback.RTCODE_SAVE_FAIL);
                    return;
                }
            }
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
            File photofile = new File(Exist, formatter.format(new Date()) + ".jpg");
            PostLog("照片路径准备完毕，保存文件");
            mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), photofile));
            mTakePhotoCallback.OnTakePhotoSuccess(photofile.getAbsolutePath());
        }
    };

    private Handler mBackgroundHandler;
    private HandlerThread mBackgroundThread;

    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }

    private void stopBackgroundThread() {
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join();
            mBackgroundThread = null;
            mBackgroundHandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //关闭摄像头
    private void closeCamera() {
        try {
            mCameraOpenCloseLock.acquire();
            //PostLog("关闭camera");
            if (null != mCameraDevice) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (null != mImageReader){
                mImageReader.close();
                mImageReader = null;
            }
            if (null != mMediaRecorder) {
                mMediaRecorder.release();
                mMediaRecorder = null;
            }
        }
        catch (Exception e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        }
        finally {
            PostLog("摄像头已关闭");
            mCameraOpenCloseLock.release();
        }
    }

    //启用日志
    public void enableLog(Handler handler){
        mLogHandler = handler;
    }

    private void PostLog(String string){
        if (mLogHandler == null){
            return;
        }
        if (string == null) {
            string = "空白日志";
        }
        Message msg = Message.obtain();
        msg.obj = string;//Message类有属性字段arg1、arg2、what...
        mLogHandler.sendMessage(msg);//sendMessage()用来传送Message类的值到mHandler
    }
}
